package com.example.dollycontrol;

import java.util.ArrayList;

import com.example.dollycontrol.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class DollySlider extends View {

	public final int RAIL_LENGTH = 1800; //in millimeters
	public final int INIT_MIN = 10;
	public final int INIT_MAX = 90;
	public final int INIT_EASE_IN = 30;
	public final int INIT_EASE_OUT = 70;
	
	private final int MARKER_OFFSET = 25;
	private final int MARKER_NEXT_BOUND = 30;
	private final RectF rect = new RectF();
	
	private MainActivity activityToNotify;
	private Paint paint = new Paint();
	private boolean timeLapseInProgress = false;
	private float timeLapseDuration = 10000; //in miliseconds
	private float dollyTravelLength = -1;
	
	private int padding = 20;
	private int lineHeight = 5;
	
	private ArrayList<SlideMark> slideMark = new ArrayList<SlideMark>();

	Bitmap handle = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal);
	Bitmap handleActive = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_pressed);
	Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.marker);
	Bitmap markerActive = BitmapFactory.decodeResource(getResources(), R.drawable.marker_active);
	
	private boolean maxHandleSelected, minHandleSelected = false;
	private float minHandlePositionRelative = INIT_MIN;
	private float maxHandlePositionRelative = INIT_MAX;
	
	/*
	 * Constructors
	 */
	
	public DollySlider(Context context, AttributeSet attrs) {
		super(context, attrs);
		drawMark(INIT_EASE_IN,false, Handler.EASE_IN);
		drawMark(INIT_EASE_OUT,false, Handler.EASE_OUT);
	}

	/*
	 * drawing method, creates slider, handles and markers
	 * @see android.view.View#onDraw(android.graphics.Canvas)
	 */
	
	@Override
	protected void onDraw(Canvas canvas){
		super.onDraw(canvas);
		
		//draw background line
		rect.set(padding,(this.getHeight()/2)-lineHeight,this.getWidth()-padding,(this.getHeight()/2)+lineHeight);
		paint.setStyle(Style.FILL);
		paint.setColor(Color.GRAY);
		canvas.drawRoundRect(rect, 5, 5, paint);
			
		//draw range bar
		rect.set(this.getWidth()*(minHandlePositionRelative/100)+(handle.getWidth()/2),(this.getHeight()/2)-lineHeight,this.getWidth()*(maxHandlePositionRelative/100)+(handle.getWidth()/2),(this.getHeight()/2)+lineHeight);
		paint.setColor(Color.rgb(51, 181, 229));
		canvas.drawRect(rect, paint);
	
		canvas.drawBitmap((maxHandleSelected?handleActive:handle), this.getWidth() * (maxHandlePositionRelative/100), (this.getHeight()/2)-(handle.getHeight()/2), paint);
		canvas.drawBitmap((minHandleSelected?handleActive:handle), this.getWidth() * (minHandlePositionRelative/100), (this.getHeight()/2)-(handle.getHeight()/2), paint);
	
		// draw markers
		for(SlideMark sM : getSlideMark()){
			canvas.drawBitmap((sM.isActive()?marker:markerActive), (this.getWidth()*(sM.getPosition()/100))-(marker.getWidth()/2), (this.getHeight()/2)-(marker.getHeight()/2)-MARKER_OFFSET, paint);
		}
	}	
			
	/*
	 * touch listener listens to touch interactions on the view and reacts accordingly
	 */
	
	@Override
	public boolean onTouchEvent(MotionEvent event){
		
		int touchX = (int)event.getX();
		int touchY = (int)event.getY();
		
		float absXHandleMax = maxHandlePositionRelative * this.getWidth() / 100.0f;
		float absXHandleMin = minHandlePositionRelative * this.getWidth() / 100.0f;
			
		switch(event.getAction()){
		
		// catch press events
		case MotionEvent.ACTION_DOWN:	
			if(!timeLapseInProgress){
				if(touchX > absXHandleMin && touchX < absXHandleMin + handle.getWidth() && touchY > (this.getHeight()/2)-(marker.getHeight()/2)){	// touch on minHandle
					minHandleSelected = true;
					maxHandleSelected = false;
				}
				else if(touchX > absXHandleMax && touchX < absXHandleMax + handle.getWidth() && touchY > (this.getHeight()/2)-(marker.getHeight()/2)){	// touch on maxHandle
					maxHandleSelected = true;
					minHandleSelected = false;
				}
				else{	// touch possibly on a marker
					SlideMark sM = getMarkerAtLocation(touchX);
					if(sM != null){
						sM.setActive(true);
					}
					else{
						for(SlideMark marker : getSlideMark()){
							marker.setActive(false);
						}
					}
				}
			}	
			break;
			
		// catch drag events
		case MotionEvent.ACTION_MOVE:
			if(!timeLapseInProgress){
				if(maxHandleSelected){
					maxHandlePositionRelative = ((100.0f/this.getWidth())*touchX)-getPercentageFromFloat(marker.getWidth()/2);
					if(maxHandlePositionRelative < minHandlePositionRelative + 3f){
						minHandlePositionRelative = maxHandlePositionRelative - 3f;
					}
					adjustMarkerPositions();
					restrictToBounds();
				}
				else if(minHandleSelected){
					minHandlePositionRelative = ((100.0f/this.getWidth())*touchX)-getPercentageFromFloat(marker.getWidth()/2);
					if(minHandlePositionRelative > maxHandlePositionRelative - 3f){
						maxHandlePositionRelative = minHandlePositionRelative + 3f;
					}
					adjustMarkerPositions();
					restrictToBounds();
				}
				else {
					SlideMark activeMark = getActiveMarker();
					if(activeMark != null){
						activeMark.setPosition((100.0f/this.getWidth())*touchX);
						restrictMarker(activeMark);
						activeMark.setRelation((maxHandlePositionRelative - minHandlePositionRelative + getPercentageFromFloat(handle.getWidth()))/(activeMark.getPosition()-minHandlePositionRelative));
					}
				}
			}
			break;	
		
		// catch 'up' events
		case MotionEvent.ACTION_UP:
			maxHandleSelected = false;
			minHandleSelected = false;
			break;
		}
		
		activityToNotify.sliderHasChangedNotification(Notification.DRAG_EVENT);
		invalidate();
		return true;
	}
	
	private void restrictMarker(SlideMark activeMark){
		if(activeMark.getPosition() < minHandlePositionRelative + 4.5f){
			activeMark.setPosition(minHandlePositionRelative + 4.5f);
		}
		else if(activeMark.getPosition() > maxHandlePositionRelative + 4.5f){
			activeMark.setPosition(maxHandlePositionRelative + 4.5f);
		}
	}
	
	private void drawMark(int position, boolean active, Handler handler){
		SlideMark newMark = new SlideMark(position,1,active, handler);
		getSlideMark().add(newMark);
		newMark.setRelation((maxHandlePositionRelative - minHandlePositionRelative)/(newMark.getPosition()-minHandlePositionRelative));
	}
	
	/*
	 *  Check if touch event is on a marker, if yes, return it, otherwise return null
	 */
	
	private SlideMark getActiveMarker(){
		for(SlideMark sM : getSlideMark()){
			if(sM.isActive()){
				return sM;
			}
		}
		return null;
	}
	
	/*
	 * if available, returns marker, null otherwise
	 */
	
	private SlideMark getMarkerAtLocation(int touchX){
		SlideMark active = null;
		for(SlideMark sM : getSlideMark()){
			sM.setActive(false);
			float absMarkerPosition = getAbsoluteFromPercentage(sM.getPosition());
			if(touchX > absMarkerPosition - MARKER_NEXT_BOUND && touchX < absMarkerPosition + MARKER_NEXT_BOUND){
				active = sM;
			}
		}
		return active;
	}
	
	/*
	 * adjust the markers to keep them relative to both handles 
	 */
	
	private void adjustMarkerPositions(){
		float distanceMinMaxHandle = maxHandlePositionRelative - minHandlePositionRelative + getPercentageFromFloat(handle.getWidth()); 
		for(SlideMark sM : getSlideMark()){
			sM.setPosition(minHandlePositionRelative + (distanceMinMaxHandle/sM.getRelation()));
		}
	}
	
	/*
	 * prevents handles to leave bounds
	 */
	
	private void restrictToBounds(){
		if(maxHandlePositionRelative < 1.5){
			maxHandlePositionRelative = 1.5f;
		}
		else if(maxHandlePositionRelative > 90){
			maxHandlePositionRelative = 90;
		}
		if(minHandlePositionRelative < 0){
			minHandlePositionRelative = 0;
		}
		else if(minHandlePositionRelative > 93.5){
			minHandlePositionRelative = 93.5f;
		}
	}
		
	/*
	 * fired from timer to advance dolly every 'period' milliseconds
	 */
	
	public void dollyStep(int period){
		if(dollyTravelLength == -1){
			dollyTravelLength = maxHandlePositionRelative - minHandlePositionRelative;
		}
		minHandlePositionRelative += dollyTravelLength/(timeLapseDuration/period);
		if(minHandlePositionRelative > maxHandlePositionRelative-1.5f){
			activityToNotify.showPopUpMessage("Timelapse Complete!");
			playSound();
			activityToNotify.resetSlider();
		}
		activityToNotify.sliderHasChangedNotification(Notification.TIMER_EVENT);
		invalidate();
	}
	
	public void playSound(){
		Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
		Ringtone r = RingtoneManager.getRingtone(activityToNotify.getApplicationContext(), notification);
		r.play();
	}
	
	/*
	 * registers an activity to be notified when the range slider changes
	 */
	
	public void registerForChangedNotification(MainActivity activity){
		this.activityToNotify = activity;
	}
	
	/*
	 * returns the absolute value from relative 
	 */
	
	public float getAbsoluteFromPercentage(float percentage){
		return (this.getWidth() * percentage) / 100.0f ;
	}
	
	/*
	 * returns relative value from absolute
	 */
	
	public float getPercentageFromFloat(float value){
		return (100.0f / this.getWidth()) * value;
	}
	
	/*
	 * Clears all markers
	 */
	
	public void deleteMarkers(){
		getSlideMark().clear();
	}
	
	public void resetMarkers(){
		for(SlideMark sM : slideMark){
			if(sM.getHandler().equals(Handler.EASE_IN)){
				sM.setPosition(INIT_EASE_IN);
			}
			else if(sM.getHandler().equals(Handler.EASE_OUT)){
				sM.setPosition(INIT_EASE_OUT);
			}
		}
	}
	
	/*
	 * Mutators
	 */
	
	public void setMinHandlePosition(float position){
		minHandlePositionRelative = position;
	}
	
	public float getMinHandle(){
		return this.minHandlePositionRelative;
	}
	
	public void setMaxHandlePosition(float position){
		maxHandlePositionRelative = position;
	}
	
	public float getMaxHandle(){
		return this.maxHandlePositionRelative;
	}

	public boolean isTimeLapseInProgress() {
		return timeLapseInProgress;
	}

	public void setTimeLapseInProgress(boolean timeLapseInProgress) {
		this.timeLapseInProgress = timeLapseInProgress;
	}
	
	public void setTimeLapseDuration(float timeLapseDuration){
		this.timeLapseDuration = timeLapseDuration;
	}
	
	public void setDollyTravelLength(float dollyTravelLength){
		this.dollyTravelLength = dollyTravelLength;
	}

	public ArrayList<SlideMark> getSlideMark() {
		return slideMark;
	}

	public void setSlideMark(ArrayList<SlideMark> slideMark) {
		this.slideMark = slideMark;
	}
}
